﻿//
//  Copyright (c) 2011 Extreme: Paweł Huryn (lead), Jakub Domański
//
using System;
using System.Collections.Generic;
using Extreme.SPFramework.DependencyInjection;

namespace Extreme.SPFramework.Cache
{
    /// <summary>
    /// Very simple singleton cache. Class is thread-safe.
    /// Expired objects exists until new values appear.
    /// </summary>
    [Service(typeof(ICache), LifestyleType.Singleton)]
    public class SimpleCache : ICache
    {
        private readonly object locker = new object();
        private readonly Dictionary<string, CacheValue> values = new Dictionary<string, CacheValue>();
        private readonly object empty = new object();

        private class CacheValue
        {
            public CacheValue(object value)
            {
                Value = value;
            }

            public CacheValue(object value, DateTime expireDate)
            {
                Value = value;
                ExpireDate = expireDate;
            }

            public bool IsExpired
            {
                get { return ExpireDate.HasValue && DateTime.Now > ExpireDate; }
            }

            public object Value { get; private set; }
            public DateTime? ExpireDate { get; private set; }
        }

        /// <summary>
        /// Empty object, returned when not in cache
        /// </summary>
        public object Empty
        {
            get { return empty; }
        }

        /// <summary>
        /// Caches object
        /// </summary>
        /// <param name="key">key</param>
        /// <param name="value">value</param>
        public void Save(string key, object value)
        {
            lock (locker)
            {
                values[key] = new CacheValue(value);
            }
        }

        /// <summary>
        /// Caches object
        /// </summary>
        /// <param name="key">key</param>
        /// <param name="value">value</param>
        /// <param name="timeout">time after which cache expires</param>
        public void Save(string key, object value, TimeSpan timeout)
        {
            lock (locker)
            {
                values[key] = new CacheValue(value, DateTime.Now.Add(timeout));
            }
        }

        /// <summary>
        /// Clears cache at specified key
        /// </summary>
        /// <param name="key">Cache key</param>
        public void Clear(string key)
        {
            lock(locker)
            {
                if (values.ContainsKey(key))
                    values.Remove(key);
            }
        }

        // Clears all cache
        public void Clear()
        {
            lock(locker)
            {
                values.Clear();
            }
        }

        /// <summary>
        /// Gets cached object or this.Empty if not in cache
        /// </summary>
        /// <param name="key">Cache key</param>
        /// <returns>Cached object or </returns>
        public object Get(string key)
        {
            // needs to be synchronized
            lock (locker)
            {
                if (values.ContainsKey(key))
                {
                    var value = values[key];
                    if (!value.IsExpired)
                        return value.Value;
                }
            }
            // returns empty
            return Empty;
        }
    }
}
